package com.yubest.demo.advice;

import cn.hutool.crypto.SecureUtil;
import com.yubest.demo.anno.SafetyProcess;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Base64;

/**
 * @Author hweiyu
 * @Description
 * @Date 2021/3/10 16:51
 */
@ControllerAdvice
public class MyRequestBodyAdvice implements RequestBodyAdvice {

    @Value("${safety.secret}")
    private String secret;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        SafetyProcess process = methodParameter.getMethodAnnotation(SafetyProcess.class);
        //如果带有安全注解且标记为解密，测进行解密操作
        return null != process && process.decode();
    }

    /**
     * 约定，请求方解密算法：
     * 1.获取源字符串
     * 2.进行base64解码
     * 3.进行ase解密
     * 4.封装可重复读的请求流对象
     * @param httpInputMessage
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     * @throws IOException
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        HttpHeaders headers = httpInputMessage.getHeaders();
        String bodyStr = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("utf-8"));
        //base64解码
        byte[] bytes = Base64.getDecoder().decode(bodyStr);
        //aes解密
        byte[] body = SecureUtil.aes(secret.getBytes()).decrypt(bytes);
        return new MyHttpInputMessage(headers, body);
    }

    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }

    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }

    /**
     * 自定义消息体，因为org.springframework.http.HttpInputMessage#getBody()只能调一次，所以要重新封装一个可重复读的消息体
     */
    @AllArgsConstructor
    public static class MyHttpInputMessage implements HttpInputMessage {

        private HttpHeaders headers;

        private byte[] body;

        @Override
        public InputStream getBody() throws IOException {
            return new ByteArrayInputStream(body);
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }
    }

}
